/*
 * (C) 2007-2010 Taobao Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *
 *
 * Authors:
 *   yanran <yanran.hfs@taobao.com>
 *     - some work details if you want
 *
 */

#ifndef _BUFFER_H__
#define _BUFFER_H__

#include <stdexcept>
#include <pthread.h>
#include <stdint.h>

struct DefaultSimpleAllocator
{
  void *malloc(const int64_t sz) {return ::malloc(sz); }
  void free(void *p) { ::free(p); }
};

template <class Alloc = DefaultSimpleAllocator>
class base_buffer
{
  public:
    static const int64_t DEFAULT_CAPACITY = BUFSIZ;
    static const bool USE_EXCEPTION = true;
    static const bool NO_EXCEPTION = false;
  protected:
    int64_t capacity_;
    int64_t length_;
    Alloc alloc_;

  protected:
    int64_t min(int64_t a, int64_t b) const {return a < b ? a : b;}

    virtual int64_t reset_size_(bool exception, int64_t size, bool retain_data) = 0;

    int64_t alloc_size_(bool exception, int64_t size, bool retain_data)
    {
      if (size > capacity_) return reset_size_(exception, size, retain_data);
      else return 0;
    }

    int64_t alloc_size_(bool exception, int64_t size) {return alloc_size_(exception, size, true);}

    void init_capacity_(int64_t capacity) {capacity_ = capacity;}
  public:
    base_buffer() {init_capacity_(DEFAULT_CAPACITY);}
    base_buffer(int64_t capacity) {init_capacity_(capacity);}
    virtual ~base_buffer() {}

  public:
    virtual char* ptr() = 0;
    virtual const char* ptr() const = 0;

    int64_t length() const {return length_;}
    int64_t& length() {return length_;}

    int64_t capacity() const {return capacity_;}
    void set_capacity(int64_t new_capacity) {reset_size_(new_capacity);}

    char at(int64_t pos) const {return ptr()[pos];}
    char& at(int64_t pos) {return ptr()[pos];}

  public:
    int64_t compare(const char* str, int64_t len) const
    {
      int64_t r = memcmp(ptr(), str, min(length(), len));
      if (0 == r)
      {
        if (length() < len) r = -1;
        else if (length() > len) r = 1;
      }
      return r;
    }
    int64_t compare(const char* str) const {return compare(str, strlen(str));}
    int64_t compare(const base_buffer& rbuf) const {return compare(rbuf.ptr(), rbuf.length());}

  public:
    base_buffer& append(const char* str, int64_t len)
    {
      alloc_size_(USE_EXCEPTION, length() + len);
      memcpy(ptr() + length(), str, len);
      length() += len;
      return *this;
    }
    base_buffer& append(const char* str) {return append(str, strlen(str));}
    base_buffer& append(const base_buffer& rbuf) {return append(rbuf.ptr(), rbuf.length());}
    base_buffer& append(int64_t num)
    {
      char buf[sizeof(int64_t) * 5];
      int64_t len = 0;
      bool positive = true;
      if (0 != num)
      {
        if (num < 0) positive = false;
        while (0 != num)
        {
          buf[len++] = num % 10 + '0';
          num /= 10;
        }
        if (!positive) buf[len++] = '-';
      }
      else
      {
        buf[0] = '0';
        len = 1;
      }

      alloc_size_(USE_EXCEPTION, length() + len);
      while (--len >= 0) ptr()[length()++] = buf[len];

      return *this;
    }
    base_buffer& append(int32_t num) {return append(static_cast<int64_t>(num));}
    base_buffer& append(int16_t num) {return append(static_cast<int64_t>(num));}
    base_buffer& append(int8_t num) {return append(static_cast<int64_t>(num));}
    base_buffer& append(uint64_t num)
    {
      char buf[sizeof(int64_t) * 5];
      int64_t len = 0;
      if (0 != num)
      {
        while (0 != num)
        {
          buf[len++] = num % 10 + '0';
          num /= 10;
        }
      }
      else
      {
        buf[0] = '0';
        len = 1;
      }

      alloc_size_(USE_EXCEPTION, length() + len);
      while (--len >= 0) ptr()[length()++] = buf[len];

      return *this;
    }
    base_buffer& append(uint32_t num) {return append(static_cast<uint64_t>(num));}
    base_buffer& append(uint16_t num) {return append(static_cast<uint64_t>(num));}
    base_buffer& append(uint8_t num) {return append(static_cast<uint64_t>(num));}
    base_buffer& append(bool v)
    {
      if (v) return append("true", 4);
      else return append("false", 5);
    }

  public:
    base_buffer& assign(const char* str, int64_t len) {length() = 0; return append(str, len);}
    base_buffer& assign(const char* str) {return assign(str, strlen(str));}
    base_buffer& assign(const base_buffer& rbuf) {return assign(rbuf.ptr(), rbuf.length());}
    base_buffer& assign(int64_t num) {length() = 0; return append(num);}
    base_buffer& assign(int32_t num) {return assign(static_cast<int64_t>(num));}
    base_buffer& assign(int16_t num) {return assign(static_cast<int64_t>(num));}
    base_buffer& assign(int8_t num) {return assign(static_cast<int64_t>(num));}
    base_buffer& assign(uint64_t num) {length() = 0; return append(num);}
    base_buffer& assign(uint32_t num) {return assign(static_cast<uint64_t>(num));}
    base_buffer& assign(uint16_t num) {return assign(static_cast<uint64_t>(num));}
    base_buffer& assign(uint8_t num) {return assign(static_cast<uint64_t>(num));}
    base_buffer& assign(bool v) {length() = 0; return append(v);}

  public:
    template <class charT>
    class iterator_base
    {
      private:
        charT* cur_;
      public:
        explicit iterator_base(charT* p) {cur_ = p;}

        template <class charOT>
        iterator_base(const iterator_base<charOT> &ri) {cur_ = ri.ptr();}

        const charT* ptr() const {return cur_;}
        iterator_base& operator++() {cur_++; return *this;}
        iterator_base operator++(int) {return iterator_base(cur_++);}
        const charT operator*() const {return *cur_;}
        charT& operator*() {return *cur_;}
        charT* operator->() {return cur_;}
        template <class charTR>
        bool operator==(iterator_base<charTR> ri) const {return ptr() == ri.ptr();}
        template <class charTR>
        bool operator!=(iterator_base<charTR> ri) const {return ptr() != ri.ptr();}
    };

    typedef iterator_base<char> iterator;
    typedef iterator_base<const char> const_iterator;

    iterator begin() {return iterator(ptr());}
    const_iterator begin() const {return const_iterator(ptr());}
    iterator end() {return iterator(ptr() + length_);}
    const_iterator end() const {return const_iterator(ptr() + length_);}
    iterator capacity_end() {return iterator(ptr() + capacity_);}
    const_iterator capacity_end() const {return const_iterator(ptr() + capacity_);}
    int64_t pos(iterator iter) const {return iter.ptr() - ptr();}
    int64_t pos(const_iterator iter) const {return iter.ptr() - ptr();}
};

template <class Alloc = DefaultSimpleAllocator>
class base_normal_buffer : public base_buffer<Alloc>
{
  protected:
    char* ptr_;

  public:
    typedef base_buffer<Alloc> Parent;

  protected:
    virtual int64_t reset_size_(bool exception, int64_t size, bool retain_data)
    {
      if (0 == ptr_)
        if (exception) throw std::runtime_error("buffer points to NULL");
        else return 1;
      char* pptr_ = ptr_;
      ptr_ = static_cast<char*>(Parent::alloc_.malloc(size));
      if (0 != ptr_)
      {
        if (retain_data) memcpy(ptr_, pptr_, min(Parent::length_, size));
        else Parent::length_ = 0;
        Parent::capacity_ = size;
        Parent::alloc_.free(pptr_);
        pptr_ = 0;
        return 0;
      }
      else
      {
        ptr_ = pptr_;
        if (exception) throw std::bad_alloc();
        else return 1;
      }
    }

    void preset_()
    {
      Parent::length_ = 0;
      ptr_ = static_cast<char*>(Parent::alloc_.malloc(Parent::capacity_));
      if (0 == ptr_)
      {
        Parent::capacity_ = 0;
        throw std::bad_alloc();
      }
    }

  public:
    base_normal_buffer() : base_buffer<Alloc>() {preset_();}
    base_normal_buffer(int64_t capacity) : base_buffer<Alloc>(capacity) {preset_();}
    virtual ~base_normal_buffer()
    {
      if (0 != ptr_)
      {
        Parent::alloc_.free(ptr_);
        ptr_ = 0;
      }
      Parent::capacity_ = 0;
      Parent::length_ = 0;
    }

  public:
    virtual char* ptr() {return ptr_;}
    virtual const char* ptr() const {return ptr_;}
};

template <class Alloc = DefaultSimpleAllocator>
class base_thread_buffer : public base_buffer<Alloc>
{
  public:
    static const pthread_key_t INVALID_THREAD_KEY = UINT32_MAX;
    typedef base_thread_buffer<Alloc> Self;
    typedef base_buffer<Alloc> Parent;
  protected:
    pthread_key_t key_;
  protected:
    virtual const char* get_ptr_(bool exception)
    {
      if (INVALID_THREAD_KEY == key_) return 0;
      else
      {
        void* mem = pthread_getspecific(key_);
        if (0 == mem)
        {
          set_specific_(exception, Parent::capacity_);
          mem = pthread_getspecific(key_);
        }
        return reinterpret_cast<const char*>(mem);
      }
    }

    virtual int64_t reset_size_(bool exception, int64_t size, bool retain_data)
    {
      const char* optr = ptr();
      if (0 == optr)
        if (exception) throw std::runtime_error("buffer points to NULL");
        else return 1;
      else
      {
        int64_t err = set_specific_(exception, size);
        if (exception || 0 == err)
        {
          if (retain_data) memcpy(ptr(), optr, Parent::min(Parent::length_, size));
          else Parent::length_ = 0;
          Parent::capacity_ = size;
          destroy_object(reinterpret_cast<void*>(const_cast<char*>(optr)));
        }
        return err;
      }
    }

    int64_t set_specific_(bool exception, int64_t size)
    {
      void* mem = 0;
      void* ptr = 0;
      mem = Parent::alloc_.malloc(sizeof(Self**) + size);
      if (0 != mem)
      {
        ptr = reinterpret_cast<void*>((reinterpret_cast<Self**>(mem)) + 1);
        if (0 != pthread_setspecific(key_, ptr))
        {
          Parent::alloc_.free(mem);
          mem = 0;
          ptr = 0;
          if (exception) throw std::runtime_error("pthread_setspecific error");
          else return 1;
        }
        else
        {
          *reinterpret_cast<Self**>(mem) = this;
          return 0;
        }
      }
      else
        if (exception) throw std::bad_alloc();
        else return 1;
    }

  public:
    base_thread_buffer() : base_buffer<Alloc>(), key_(INVALID_THREAD_KEY) {create_key();}
    base_thread_buffer(int64_t capacity)
      : base_buffer<Alloc>(capacity), key_(INVALID_THREAD_KEY) {create_key();}
    virtual ~base_thread_buffer() {delete_key();}

    static void destroy_object(void *mem)
    {
      if (0 != mem)
      {
        Self **h = (reinterpret_cast<Self**>(mem) - 1);
        (*h)->alloc_.free(h);
      }
    }
    void create_key()
    {
      if (INVALID_THREAD_KEY == key_) pthread_key_create(&key_, destroy_object);
    }
    void delete_key()
    {
      if (INVALID_THREAD_KEY != key_)
      {
        void* mem = pthread_getspecific(key_);
        if (0 != mem) destroy_object(mem);
        pthread_key_delete(key_);
      }
      key_ = INVALID_THREAD_KEY;
    }

  public:
    virtual char* ptr() {return const_cast<char*>(get_ptr_(Parent::USE_EXCEPTION));}
    virtual const char* ptr() const
    {
      if (INVALID_THREAD_KEY == key_) return 0;
      else return reinterpret_cast<const char*>(pthread_getspecific(key_));
    }
};

template <int N = 256, class Alloc = DefaultSimpleAllocator>
class sbuffer : public base_buffer<Alloc>
{
  protected:
    char buff_[N];

  protected:
    virtual int64_t reset_size_(bool exception, int64_t size, bool retain_data)
    {
      if (exception) throw std::runtime_error("stack buffer can not reset_size_");
      else return 1;
    }

  public:
    sbuffer() : base_buffer<Alloc>(N) {}
    virtual ~sbuffer() {}

  public:
    virtual char* ptr() {return buff_;}
    virtual const char* ptr() const {return buff_;}
};

typedef base_normal_buffer<> buffer;
typedef base_thread_buffer<> tbuffer;

#endif // _BUFFER_H__
